using System;
using System.Xml;
using System.Diagnostics;
using System.Collections;
using System.IO;
using System.Collections.Specialized;
using System.Text;
using Widgetsphere.Core.Util;
using Widgetsphere.Core;
using Widgetsphere.Core.Exceptions;

namespace Widgetsphere.Core.Logging
{
	public partial class MessageLog : IDisposable
	{
		#region constants
		internal const string LOG_ERROR_SOURCE = "WidgetsphereLogger";
		#endregion

		#region member variables
		private static MessageLog mInstance;
		private ILogConfiguration mLogConfiguration;
		private static ArrayList mLogClasses = new ArrayList();
		#endregion

		#region Setup and Initialize
		public static void AddLogClass(Type logClassType)
		{
			try
			{
				if (ReflectionHelper.ImplementsInterface(logClassType, typeof(ILogClass)))
				{
					mLogClasses.Add(logClassType);
					if (mInstance != null)
					{
						mInstance.mLogConfiguration_LogConfigurationChanged(null, new System.EventArgs());
					}
					else
					{
						InitializeLog(new DefaultLogConfiguration());
						mInstance.mLogConfiguration_LogConfigurationChanged(null, new System.EventArgs());
					}

				}
				else
				{
					LogLogFailure(String.Format("Cannot Add Log Class Type: {0} does not implement interface ILogClass.", logClassType.FullName));
				}
			}
			catch (Exception ex)
			{
				if (logClassType != null)
					LogLogFailure("AddLogClass(Type logClassType:" + logClassType.ToString() + ")" + Environment.NewLine + ex.ToString());
				else
					LogLogFailure("AddLogClass(Type logClassType: null)" + Environment.NewLine + ex.ToString());
			}
		}

		public static void InitializeLog(ILogConfiguration logConfiguration)
		{
			try
			{
				if (mInstance != null)
				{
					DisposeInstance();
				}
				mInstance = new MessageLog(logConfiguration);
			}
			catch (Exception ex)
			{
				LogLogFailure("public static void InitializeLog(ILogConfiguration " + logConfiguration.GetType().FullName + ")" + Environment.NewLine + ex.ToString());
			}
		}

		private MessageLog(ILogConfiguration logConfiguration)
		{
			mLogConfiguration = logConfiguration;
			try
			{
				InitializeConfigFile();
			}
			catch (Exception ex)
			{
				LogLogFailure(ex.ToString());
			}
		}

		private void InitializeConfigFile()
		{
			mLogConfiguration.LogConfigurationChanged += new EventHandler(mLogConfiguration_LogConfigurationChanged);
			mLogConfiguration_LogConfigurationChanged(null, new System.EventArgs());
		}
		#endregion

		#region TraceStatements
		
		private static void Log(TraceLevel level, string message)
		{
			//string traceLevelString;
			//if (level == TraceLevel.Off)
			//{
			//  traceLevelString = "Always";
			//  traceLevelString = traceLevelString.PadRight(7);
			//}
			//else
			//{
			//  traceLevelString = level.ToString();
			//  traceLevelString = traceLevelString.PadRight(7);
			//}
			//string logString = String.Format("{0}:{1}:{2}: {3}", traceLevelString, DateTime.UtcNow.ToString("yyyyMMdd.HHmmss"), "(" + DateTime.Now.ToString("yyyyMMdd.HHmmss:fffff ") + ")", message);
			//Trace.WriteLine(logString);
		}

		private static void Log(TraceLevel level, string message, object arg1)
		{
			string logString = String.Format(message, arg1);
			Log(level, logString);
		}

		private static void Log(TraceLevel level, string message, object arg1, object arg2)
		{
			string logString = String.Format(message, arg1, arg2);
			Log(level, logString);
		}

		private static void Log(TraceLevel level, string message, object arg1, object arg2, object arg3)
		{
			string logString = String.Format(message, arg1, arg2, arg3);
			Log(level, logString);
		}

		private static void Log(TraceLevel level, string message, object[] args)
		{
			string logString = String.Format(message, args);
			Log(level, logString);
		}
		#endregion

		#region LogAlways
		public static void LogAlways(string message)
		{
			Log(TraceLevel.Off, message);
		}

		public static void LogAlways(string message, object arg1)
		{
			Log(TraceLevel.Off, message, arg1);
		}

		public static void LogAlways(string message, object arg1, object arg2)
		{
			Log(TraceLevel.Off, message, arg1, arg2);
		}

		public static void LogAlways(string message, object arg1, object arg2, object arg3)
		{
			Log(TraceLevel.Off, message, arg1, arg2, arg3);
		}

		public static void LogAlways(string message, object[] args)
		{
			Log(TraceLevel.Off, message, args);
		}
		#endregion

		#region LogError
		public static void LogError(TraceSwitch traceSwitch, string message)
		{
			if (traceSwitch.TraceError)
			{
				Log(TraceLevel.Error, message);
			}
		}

		public static void LogError(TraceSwitch traceSwitch, string message, object arg1)
		{
			if (traceSwitch.TraceError)
			{
				Log(TraceLevel.Error, message, arg1);
			}
		}

		public static void LogError(TraceSwitch traceSwitch, string message, object arg1, object arg2)
		{
			if (traceSwitch.TraceError)
			{
				Log(TraceLevel.Error, message, arg1, arg2);
			}
		}

		public static void LogError(TraceSwitch traceSwitch, string message, object arg1, object arg2, object arg3)
		{
			if (traceSwitch.TraceError)
			{
				Log(TraceLevel.Error, message, arg1, arg2, arg3);
			}
		}

		public static void LogError(TraceSwitch traceSwitch, string message, object[] args)
		{
			if (traceSwitch.TraceError)
			{
				Log(TraceLevel.Error, message, args);
			}
		}


		public static void LogError(TraceSwitch traceSwitch, Exception ex)
		{
			LogError(traceSwitch, ex.ToString());
		}

		public static void LogError(TraceSwitch traceSwitch, Exception ex, NameValueCollection error)
		{
			StringBuilder errorMsg = new StringBuilder();
			foreach (string name in error.Keys)
			{
				errorMsg.AppendFormat("{0} : {1}\n", name, error[name]);
			}
			errorMsg.Append(ex.ToString());
			LogError(traceSwitch, errorMsg.ToString());
		}

		public static void LogError(TraceSwitch traceSwitch, Exception ex, string error)
		{
			LogError(traceSwitch, "{0}\n{1}", error, ex.ToString());
		}
		#endregion

		#region LogWarning
		public static void LogWarning(TraceSwitch traceSwitch, string message)
		{
			if (traceSwitch.TraceWarning)
			{
				Log(TraceLevel.Warning, message);
			}
		}

		public static void LogWarning(TraceSwitch traceSwitch, string message, object arg1)
		{
			if (traceSwitch.TraceWarning)
			{
				Log(TraceLevel.Warning, message, arg1);
			}
		}

		public static void LogWarning(TraceSwitch traceSwitch, string message, object arg1, object arg2)
		{
			if (traceSwitch.TraceWarning)
			{
				Log(TraceLevel.Warning, message, arg1, arg2);
			}
		}

		public static void LogWarning(TraceSwitch traceSwitch, string message, object arg1, object arg2, object arg3)
		{
			if (traceSwitch.TraceWarning)
			{
				Log(TraceLevel.Warning, message, arg1, arg2, arg3);
			}
		}

		public static void LogWarning(TraceSwitch traceSwitch, string message, object[] args)
		{
			if (traceSwitch.TraceWarning)
			{
				Log(TraceLevel.Warning, message, args);
			}
		}

		public static void LogWarning(TraceSwitch traceSwitch, Exception ex)
		{
			LogError(traceSwitch, ex.ToString());
		}

		public static void LogWarning(TraceSwitch traceSwitch, Exception ex, string error)
		{
			LogError(traceSwitch, "{0}\n{1}", error, ex.ToString());
		}
		#endregion

		#region LogInfo
		public static void LogInfo(TraceSwitch traceSwitch, string message)
		{
			if (traceSwitch.TraceInfo)
			{
				Log(TraceLevel.Info, message);
			}
		}

		public static void LogInfo(TraceSwitch traceSwitch, string message, object arg1)
		{
			if (traceSwitch.TraceInfo)
			{
				Log(TraceLevel.Info, message, arg1);
			}
		}

		public static void LogInfo(TraceSwitch traceSwitch, string message, object arg1, object arg2)
		{
			if (traceSwitch.TraceInfo)
			{
				Log(TraceLevel.Info, message, arg1, arg2);
			}
		}

		public static void LogInfo(TraceSwitch traceSwitch, string message, object arg1, object arg2, object arg3)
		{
			if (traceSwitch.TraceInfo)
			{
				Log(TraceLevel.Info, message, arg1, arg2, arg3);
			}
		}

		public static void LogInfo(TraceSwitch traceSwitch, string message, object[] args)
		{
			if (traceSwitch.TraceInfo)
			{
				Log(TraceLevel.Info, message, args);
			}
		}
		#endregion

		#region LogVerbose
		public static void LogVerbose(TraceSwitch traceSwitch, string message)
		{
			if (traceSwitch.TraceVerbose)
			{
				Log(TraceLevel.Verbose, message);
			}
		}

		public static void LogVerbose(TraceSwitch traceSwitch, string message, object arg1)
		{
			if (traceSwitch.TraceVerbose)
			{
				Log(TraceLevel.Verbose, message, arg1);
			}
		}

		public static void LogVerbose(TraceSwitch traceSwitch, string message, object arg1, object arg2)
		{
			if (traceSwitch.TraceVerbose)
			{
				Log(TraceLevel.Verbose, message, arg1, arg2);
			}
		}

		public static void LogVerbose(TraceSwitch traceSwitch, string message, object arg1, object arg2, object arg3)
		{
			if (traceSwitch.TraceVerbose)
			{
				Log(TraceLevel.Verbose, message, arg1, arg2, arg3);
			}
		}

		public static void LogVerbose(TraceSwitch traceSwitch, string message, object[] args)
		{
			if (traceSwitch.TraceVerbose)
			{
				Log(TraceLevel.Verbose, message, args);
			}
		}
		#endregion

		#region Log Log Failure
		public static void LogLogFailure(string message)
		{
			string logName = "Application";
			if (!EventLog.SourceExists(LOG_ERROR_SOURCE))
			{
				EventLog.CreateEventSource(LOG_ERROR_SOURCE, logName);
			}
			else
			{
				logName = EventLog.LogNameFromSourceName(LOG_ERROR_SOURCE, System.Environment.MachineName);
			}
			EventLog applicationLog = new EventLog(logName);
			applicationLog.Source = LOG_ERROR_SOURCE;
			applicationLog.WriteEntry(message, EventLogEntryType.Warning);
		}
		#endregion

		#region File System Watcher
		private void mLogConfiguration_LogConfigurationChanged(object sender, System.EventArgs e)
		{
			try
			{
				MessageLog.LogAlways("Called: Log.mLogConfiguration_LogConfigurationChanged");
				ResetProperties();
				ResetSwitches();
				ResetListeners();
			}
			catch (Exception ex)
			{
				MessageLog.LogLogFailure("Configuration File Cannot Be Parsed Change Was Invalid\n" + ex.ToString());
			}
		}

		private void ResetProperties()
		{
			try
			{
				MessageLog.LogAlways("Called: private void ResetProperties()");
				Trace.AutoFlush = mLogConfiguration.AutoFlush;
				Trace.IndentSize = mLogConfiguration.IndentSize;
			}
			catch (Exception ex)
			{
				LogLogFailure("Error resetting properties.\n" + ex.ToString());
			}
		}

		private void ResetSwitches()
		{
			try
			{
				MessageLog.LogAlways("Called: private void ResetSwitches()");
				foreach (Type logClass in mLogClasses)
				{
					foreach (System.Reflection.FieldInfo traceSwitch in ReflectionHelper.GetFieldsByAttribute((Type)logClass, typeof(TraceSwitchAttribute)).Keys)
					{
						TraceSwitch currentSwitch = (TraceSwitch)traceSwitch.GetValue(logClass);
						try
						{
							currentSwitch.Level = mLogConfiguration.GetTraceLevel(currentSwitch.DisplayName);
						}
						catch (Exception ex)
						{
							LogLogFailure("Error Setting Level:\n" + ex.ToString());
							try
							{
								LogLogFailure("Switch: " + currentSwitch.DisplayName);
								currentSwitch.Level = TraceLevel.Off;
							}
							catch (Exception)
							{
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				LogLogFailure("Error resetting switches.\n" + ex.ToString());
			}
		}

		private void ResetListeners()
		{
			try
			{
				MessageLog.LogAlways("Called: private void ResetListeners() Pre Listener Count : {0}", Trace.Listeners.Count);
				MessageLog.LogAlways("Configuraiton Listener Count : {0}", mLogConfiguration.TraceListeners.Count);
				RemoveListeners();
				AddListeners();
				MessageLog.LogAlways("Post Listener Count : {0}", Trace.Listeners.Count);
			}
			catch (Exception ex)
			{
				LogLogFailure("Error resetting listeners.\n" + ex.ToString());
			}
		}

		public class ListenerCompare
		{
			string _compareName;

			public ListenerCompare(string compareName)
			{
				_compareName = compareName;
			}

			public bool Match(TraceListener listener)
			{
				return StringHelper.Match(_compareName, listener.Name);
			}
		}

		private void RemoveListeners()
		{
			for (int i = Trace.Listeners.Count - 1; i >= 0; --i)
			{
				try
				{
					TraceListener listener = Trace.Listeners[i];
					if (!mLogConfiguration.TraceListeners.Exists(new ListenerCompare(listener.Name).Match))
					{
						listener.Flush();
						Trace.Listeners.Remove(Trace.Listeners[i]);
						listener.Dispose();
					}
				}
				catch (Exception ex)
				{
					LogLogFailure("Error removing listener\n" + ex.ToString());
				}
			}
		}

		private void AddListeners()
		{
			foreach (TraceListener traceListener in mLogConfiguration.TraceListeners)
			{
				string listenerName = traceListener.Name;
				try
				{
					if (!ListenerExists(listenerName))
					{
						Trace.Listeners.Add(traceListener);
					}
				}
				catch (Exception ex)
				{
					MessageLog.LogAlways("Cannot Add One Of The Listeners: " + listenerName + "Exception:" + ex.ToString());
				}
			}
		}

		private bool ListenerExists(string listenerName)
		{
			bool retVal = false;
			foreach (TraceListener tl in Trace.Listeners)
			{
				if (StringHelper.Match(listenerName, tl.Name))
				{
					retVal = true;
				}
			}
			return retVal;
		}

		public static TraceListener CreateListener(string listenerName, string typeString, string initializationData)
		{
			MessageLog.LogAlways("CreateListener(string listenerName:{0}, string typeString:{1}, string initializationData:{2})", listenerName, typeString, initializationData);
			TraceListener retVal = null;
			if (typeString == ("System.Diagnostics.TextWriterTraceListener"))
			{
				retVal = new TextWriterTraceListener(initializationData);
			}
			else if (typeString == ("System.Diagnostics.EventLogTraceListener"))
			{
				retVal = new EventLogTraceListener(initializationData);
			}
			else
			{
				retVal = (TraceListener)ReflectionHelper.CreateInstance(Type.GetType(typeString), new object[] { initializationData });
			}
			if (retVal != null)
			{
				retVal.Name = listenerName;
			}
			else
			{
				throw new Widgetsphere.Core.Exceptions.WSException(String.Format("Could not create listener - listenerName:{0}- typeString:{1})", listenerName, typeString));
			}
			return retVal;
		}


		public static TraceListener CreateListener(string listenerName, string typeString)
		{
			MessageLog.LogAlways("CreateListener(string listenerName:{0}, string typeString:{1})", listenerName, typeString);
			TraceListener retVal = null;

			if (typeString == ("System.Diagnostics.DefaultTraceListener"))
			{
				retVal = new DefaultTraceListener();
			}
			else if (typeString == ("System.Diagnostics.EventLogTraceListener"))
			{
				retVal = new EventLogTraceListener();
			}
			else
			{
				retVal = (TraceListener)ReflectionHelper.CreateInstance(Type.GetType(typeString));
			}

			if (retVal != null)
			{
				retVal.Name = listenerName;
			}
			else
			{
				throw new Widgetsphere.Core.Exceptions.WSException(String.Format("Could not create listener - listenerName:{0}- typeString:{1})", listenerName, typeString));
			}
			return retVal;
		}
		#endregion

		#region Dispose Stuff

		public static void DisposeInstance()
		{
			if (mInstance != null)
				mInstance.Dispose();
		}

		/// <summary>
		/// Disposes and clears all trace listeners.
		/// </summary>
		private bool _disposed = false;
		protected void Dispose(bool disposing)
		{
			try
			{
				if (!_disposed)
				{
					foreach (TraceListener listener in Trace.Listeners)
						listener.Dispose();
					Trace.Listeners.Clear();
					if (mLogConfiguration != null)
						mLogConfiguration.Dispose();
					_disposed = true;
				}
			}
			catch
			{
				/* eat the exception */
			}
		}

		public void Dispose()
		{
			Dispose(true);
		}

		~MessageLog()
		{
			Dispose(false);
		}

		#endregion

	}

	#region class TraceSwitch
	public partial class TraceSwitch : System.Diagnostics.TraceSwitch
	{
		public TraceSwitch(string name, string description)
			: base(name, description)
		{
		}
	}
	#endregion

}



